﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Text;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Windows.Forms;

namespace Dot5Demo.Controls
{
    public class XToolTipControl : Component
    {
        #region Properties 属性

        /// <summary>
        /// 确定在工具提示窗口出现前，指针必须在工具提示区域内静止保持的时间
        /// </summary>
        [Obsolete("无用")]
        [Browsable(false), DefaultValue(500)]
        public int InitialDelay { set; get; }

        /// <summary>
        /// 工具提示保持可见的时间期限，自动关闭延迟
        /// </summary>
        /// <returns>
        /// 当指针在控件上保持静止时，<see cref="T:System.Windows.Forms.ToolTip"/> 保持可见的时间期限（以毫秒为单位）。默认值为 5000。
        /// </returns>
        /// <filterpriority>1</filterpriority>
        [DefaultValue(5000)]
        public int AutoCloseDelay { set; get; }

        /// <summary>
        /// 边框颜色
        /// </summary>
        [DefaultValue(typeof(Color), "LightBlue")]
        public Color BorderColor { get; set; }

        /// <summary>
        /// 获取或设置一个值表示能否显示ToolTip
        /// </summary>
        [DefaultValue(true)]
        public bool CanShow
        {
            get { return canShow; }
            set
            {
                if (value != canShow)
                {
                    canShow = value;
                    if (!value)
                    {
                        //this.hasShowed = false;
                        //millisecond = 0;
                        this.Hide();
                    }
                }
            }
        }

        /// <summary>
        /// 鼠标移上是否关闭
        /// </summary>
        [Obsolete("废弃", true),
        Browsable(false),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool MoveCLose { get; set; }

        /// <summary>
        /// 句柄是否已创建
        /// </summary>
        [Browsable(false)]
        [Obsolete("命名错误，改用IsHandleCreated")]
        public bool IsHandleCreate
        {
            get { return IsHandleCreated; }
        }

        /// <summary>
        /// 句柄是否已创建
        /// </summary>
        [Browsable(false)]
        public bool IsHandleCreated
        {
            get { return window != null && window.Handle != IntPtr.Zero; }
        }

        /// <summary>
        /// 鼠标提示是否正在显示中
        /// </summary>
        [Browsable(false)]
        public bool IsShowing
        {
            get
            {
                bool isShowing = IsHandleCreated && NativeMethods.IsWindowVisible(this.Handle);
                return isShowing;
            }
        }

        /// <summary>
        /// 文本最大宽度
        /// </summary>
        [DefaultValue(286)]
        public int MaxTipWidth
        {
            get//{return (int)API.SendMessage(this.Handle,(int)NativeMethods.TTM_GETMAXTIPWIDTH,0,0);}
            {
                return this.maxTipWidth;
            }
            set
            {
                if (this.maxTipWidth != value)
                {
                    NativeMethods.SendMessage(this.Handle, NativeMethods.TTM_SETMAXTIPWIDTH, 0, value);
                    this.maxTipWidth = value;
                }
            }
        }

        /// <summary>
        /// 提示窗口句柄
        /// </summary>
        [Browsable(false)]
        public IntPtr Handle
        {
            get { return this.window == null ? IntPtr.Zero : this.window.Handle; }
        }

        /// <summary>
        /// 鼠标提示文本
        /// </summary>
        [DefaultValue(""),]
        public string Text { set; get; }

        /// <summary>
        /// 鼠标提示的位置
        /// </summary>
        protected Point Location { set; get; }

        /// <summary>
        /// 设置是否始终显示，即使父控件处于非激活状态
        /// </summary>
        [DefaultValue(false)]
        public bool ShowAlways
        {
            get
            {
                return showAlways;
            }
            set
            {
                if (showAlways != value)
                {
                    showAlways = value;
                    if (IsHandleCreated)
                    {
                        RecreateHandle();
                    }
                }
            }
        }

        /// <summary>
        /// 是否为气泡样式
        /// </summary>
        [DefaultValue(false)]
        public bool IsBalloon
        {
            get
            {
                return isBalloon;
            }

            set
            {
                if (isBalloon != value)
                {
                    isBalloon = value;
                    if (IsHandleCreated)
                    {
                        RecreateHandle();
                    }
                }
            }
        }

        /// <summary>
        /// 当设置为true时，显示或隐藏鼠标提示时将使用动画效果
        /// </summary>
        [DefaultValue(false)]
        public bool UseAnimation
        {
            get { return useAnimation; }
            set
            {
                if (useAnimation != value)
                {
                    useAnimation = value;
                    if (IsHandleCreated)
                    {
                        RecreateHandle();
                    }
                }
            }
        }

        /// <summary>
        /// 当设置为true时，显示或隐藏鼠标提示时将使用渐隐效果
        /// </summary>
        [DefaultValue(false)]
        public bool UseFading
        {
            get { return useFading; }
            set
            {
                if (useFading != value)
                {
                    useFading = value;
                    if (IsHandleCreated)
                    {
                        RecreateHandle();
                    }
                }
            }
        }

        /// <summary>
        /// 是否显示阴影
        /// </summary>
        [DefaultValue(false)]
        public bool HasDropShadow
        {
            get { return hasDropShadow; }
            set
            {
                if (hasDropShadow != value)
                {
                    hasDropShadow = value;
                    if (IsHandleCreated)
                    {
                        RecreateHandle();
                    }
                }
            }
        }

        /// <summary>
        /// 背景色
        /// </summary>
        [DefaultValue(typeof(Color), "White")]
        public Color BackColor
        {
            get { return backColor; }
            set
            {
                backColor = value;
                if (IsHandleCreated)
                {
                    NativeMethods.SendMessage(this.Handle, NativeMethods.TTM_SETTIPBKCOLOR, ColorTranslator.ToWin32(backColor), 0);
                }
            }
        }

        /// <summary>
        /// 前景色代表鼠标提示正文字体颜色
        /// </summary>
        [DefaultValue(typeof(Color), "47, 47, 47")]
        public Color ForeColor
        {
            get { return foreColor; }
            set
            {
                if (value.IsEmpty)
                {
                    throw new ArgumentException("鼠标提示颜色为空，ForeColor");
                }

                foreColor = value;
                if (IsHandleCreated)
                {
                    NativeMethods.SendMessage(this.Handle, NativeMethods.TTM_SETTIPTEXTCOLOR, ColorTranslator.ToWin32(foreColor), 0);
                }
            }
        }

        /// <summary>
        /// 鼠标提示图标
        /// </summary>
        [DefaultValue(ToolTipIcon.None)]
        public ToolTipIcon ToolTipIcon
        {
            get { return toolTipIcon; }
            set
            {
                if (toolTipIcon != value)
                {
                    //valid values are 0x0 to 0x3
                    if (!IsEnumValid(value, (int)value, (int)ToolTipIcon.None, (int)ToolTipIcon.Error))
                    {
                        throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolTipIcon));
                    }
                    toolTipIcon = value;
                    if (toolTipIcon > 0 && IsHandleCreated)
                    {
                        // If the title is null/empty, the icon won't display.
                        string title = !String.IsNullOrEmpty(this._title) ? this._title : " ";
                        NativeMethods.SendMessage(this.Handle, NativeMethods.TTM_SETTITLE, (int)toolTipIcon, title);

                        // Tooltip need to be updated to reflect the changes in the icon because
                        // this operation directly affects the size of the tooltip
                        NativeMethods.SendMessage(this.Handle, NativeMethods.TTM_UPDATE, 0, 0);
                    }
                }
            }
        }

        private static bool IsEnumValid(Enum enumValue, int value, int minValue, int maxValue)
        {
            bool valid = (value >= minValue) && (value <= maxValue);

            return valid;
        }

        /// <summary>
        /// 鼠标提示标题
        /// </summary>
        [DefaultValue("")]
        public string Title
        {
            get { return _title; }
            set
            {
                if (value == null)
                {
                    value = string.Empty;
                }

                if (_title != value)
                {
                    _title = value;
                    if (IsHandleCreated)
                    {
                        NativeMethods.SendMessage(this.Handle, NativeMethods.TTM_SETTITLE, (int)toolTipIcon, _title);

                        // Tooltip need to be updated to reflect the changes in the titletext because
                        // this operation directly affects the size of the tooltip
                        NativeMethods.SendMessage(this.Handle, NativeMethods.TTM_UPDATE, 0, 0);
                    }
                }
            }
        }

        /// <summary>
        /// 设置鼠标提示是否置顶。
        /// 对于置顶的对话框，如果需要鼠标提示时，需要设置为true，
        /// 否则会被窗体本身遮盖。
        /// </summary>
        [DefaultValue(false)]
        public bool TopMost
        {
            get { return _topMost; }
            set
            {
                if (TopMost != value)
                {
                    _topMost = value;
                    if (IsHandleCreated)
                        RecreateHandle();
                }
            }
        }

        /// <summary>
        /// 字体。
        /// </summary>
        /// <remarks>
        /// 当改为自行计算大小，自行绘制时，已经无需使用Window的字体。
        /// 目前不是公共属性，不公开。
        /// 2018-05-30 由于医嘱需要自定义鼠标提示字号，因此公开为public
        /// </remarks>
        public Font Font
        {
            get { return _font; }
            set { _font = value ?? new Font("微软雅黑", 12.0f); }
        }

        #endregion Properties 属性

        #region Field 字段

        private bool canShow;

        private int maxTipWidth = 286;

        private TootipNativeWindow window;

        /// <summary>
        /// 定时器
        /// </summary>
        private Timer _timer;

        /// <summary>
        /// 默认计时器间隔时间
        /// </summary>
        private const int DefaultTimerInterval = 100;

        /// <summary>
        /// 经过的毫秒数
        /// </summary>
        //private long millisecond;

        /// <summary>
        /// 记录是否显示过
        /// </summary>
        //private bool hasShowed;

        private bool useAnimation = true;
        private bool useFading = true;
        private bool isBalloon = false;
        private bool showAlways = false;
        private bool hasDropShadow = true;

        private bool ownerDraw = true;

        private bool _topMost = false;

        private Color backColor = SystemColors.Info;
        private Color foreColor = SystemColors.InfoText;
        private ToolTipIcon toolTipIcon = ToolTipIcon.None;
        private string _title = "";

        private Size _tipSize;
        private Size _titleSize;
        private Size _textSize;
        private Font _font;

        #endregion Field 字段

        #region Contruct

        public XToolTipControl()
        {
            this.window = new TootipNativeWindow(this);

            this.Text = "";
            this._title = "";
            this.Font = new Font("微软雅黑", 12.0f);

            this.canShow = true;
            this.maxTipWidth = 286;
            //this.InitialDelay = 500;
            this.AutoCloseDelay = 5000;

            this._timer = new Timer();
            // Tick事件的触发间隔为100毫秒
            this._timer.Interval = DefaultTimerInterval;
            //this.MoveClose = true;
            this._timer.Tick += new EventHandler(TimerHandler);

            this.useAnimation = false;
            this.useFading = false;
            this.isBalloon = false;
            this.showAlways = false;
            this.hasDropShadow = false;
            this._topMost = false;

            this.backColor = Color.White;
            this.foreColor = Color.FromArgb(47, 47, 47);

            this.BorderColor = Color.LightBlue;  //Color.LightSteelBlue;
        }

        #endregion Contruct

        #region Method

        /// <summary>
        /// 隐藏鼠标提示
        /// </summary>
        public void Hide()
        {
            if (window == null)
            {
                return;
            }

            // VSWhidbey 562045: Only send message if we actually have a ToolTip at this point.
            if (IsHandleCreated)
            {
                NativeMethods.TOOLINFO_TOOLTIP ti = GetTOOLINFO(IntPtr.Zero, null);
                try
                {
                    NativeMethods.SendMessage(this.Handle, NativeMethods.TTM_TRACKACTIVATE, 0, ti);
                    NativeMethods.SendMessage(this.Handle, NativeMethods.TTM_DELTOOL, 0, ti);
                }
                finally
                {
                    if (ti.lpszText != IntPtr.Zero)
                        Marshal.FreeHGlobal(ti.lpszText);
                }
            }
            StopTimer();

            //this.CanShow = false;

            // handle将在Dispose时销毁，所以关闭时无需销毁
            // hide时销毁句柄有助于优化句柄数量，因为很多人写代码不讲究，创建了一堆的Tooltip
            DestroyHandle();
        }

        /// <summary>
        /// 移除字符串首尾的控制字符，以免影响大小计算，导致计算大小和绘制大小不一致
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        private static string NormalizeTooltipText(string text)
        {
            return text == null ? null : text.Trim(new char[] { '\r', '\n' });
        }

        /// <summary>
        /// 显示鼠标提示
        /// </summary>
        /// <param name="title">标题</param>
        /// <param name="text">提示文本</param>
        /// <param name="icon">图标类型</param>
        /// <param name="location">显示坐标，左上角</param>
        /// <remarks>
        /// 鼠标提示相对于参考矩形区域来决定位置，例如给定一个参考矩形区域，
        /// 参考区域的锚点位置为左下角，鼠标提示的锚点位置为左上角，
        /// 则提示的左上角和矩形的左下角对齐，效果为鼠标提示显示在参考矩形区域的左下方
        /// </remarks>
        private void Show(string title, string text, ToolTipIcon icon, Point location)
        {
            this.Text = NormalizeTooltipText(text);
            this.Title = title;
            this.toolTipIcon = icon;

            if (this.IsHandleCreated)
                this.Close();

            this.CreateHandle();

            _tipSize = GetToolTipSize();
            this.Location = location;

            this.ActivateTip();
        }

        /// <summary>
        /// 显示鼠标提示
        /// </summary>
        /// <param name="title">标题</param>
        /// <param name="text">提示文本</param>
        /// <param name="icon">图标类型</param>
        /// <param name="bounds">相对区域</param>
        /// <param name="location">相对位置</param>
        /// <param name="tipLocation">提示位置</param>
        /// <remarks>
        /// 鼠标提示相对于参考矩形区域来决定位置，例如给定一个参考矩形区域，
        /// 参考区域的锚点位置为左下角，鼠标提示的锚点位置为左上角，
        /// 则提示的左上角和矩形的左下角对齐，效果为鼠标提示显示在参考矩形区域的左下方
        /// </remarks>
        private void Show(string title, string text, ToolTipIcon icon, Rectangle bounds,
            Position location, Position tipLocation)
        {
            this.Text = NormalizeTooltipText(text);
            this.Title = title;
            this.toolTipIcon = icon;

            if (this.IsHandleCreated)
                this.Close();

            this.CreateHandle();

            //要计算相对位置，必须预先获取鼠标提示的大小
            _tipSize = GetToolTipSize();
            Point relPoint = GetRelativePoint(bounds, _tipSize, location, tipLocation);
            this.Location = relPoint;

            this.ActivateTip();
        }

        /// <summary>
        /// 直接在屏幕上显示鼠标提示，注意每次调用都会显示
        /// </summary>
        /// <param name="text"></param>
        /// <param name="location"></param>
        public void Show(string text, Point location)
        {
            if (this.canShow)
            {
                this.Show(this.Title, text, this.ToolTipIcon, location);
                StopTimer();
                StartTimer();
            }
        }

        /// <summary>
        /// 显示鼠标提示，指定需要鼠标提示的目标区域，提供相对位置来决定提示位置。
        /// </summary>
        /// <param name="text">提示文本</param>
        /// <param name="referBounds">参考矩形区域</param>
        /// <param name="referLocation">参考区域的锚点位置</param>
        /// <param name="tipLocation">提示的锚点位置</param>
        /// <remarks>
        /// 鼠标提示相对于参考矩形区域来决定位置，例如给定一个参考矩形区域，
        /// 参考区域的锚点位置为左下角，鼠标提示的锚点位置为左上角，
        /// 则提示的左上角和矩形的左下角对齐，效果为鼠标提示显示在参考矩形区域的左下方
        /// </remarks>
        public void Show(string text, Rectangle referBounds, Position referLocation, Position tipLocation)
        {
            if (this.canShow)
            {
                this.Show(this.Title, text, this.ToolTipIcon, referBounds, referLocation, tipLocation);
                StopTimer();
                StartTimer();
            }
        }

        /// <summary>
        /// 环绕指定区域显示鼠标提示
        /// </summary>
        /// <param name="text">提示文本</param>
        /// <param name="referBounds">参考区域</param>
        public void Show(string text, Rectangle referBounds)
        {
            this.Show(text, referBounds, Position.BottomRight, Position.BottomLeft);
        }

        /// <summary>
        /// 激活鼠标提示
        /// </summary>
        private void ActivateTip()
        {
            if (!IsHandleCreated)
                throw new InvalidOperationException("Handle not created !");

            var windowHandle = this.window.Handle;
            Point location = this.Location;

            NativeMethods.SendMessage(windowHandle, NativeMethods.TTM_SETMAXTIPWIDTH, 0, this.maxTipWidth);

            NativeMethods.TOOLINFO_TOOLTIP toolinfo = GetTOOLINFO(IntPtr.Zero, this.Text);
            toolinfo.hwnd = windowHandle;
            try
            {
                NativeMethods.SendMessage(windowHandle, NativeMethods.TTM_ADDTOOL, 0, toolinfo);

                int lparam = NativeMethods.MAKELONG(location.X, location.Y);
                NativeMethods.SendMessage(windowHandle, NativeMethods.TTM_TRACKPOSITION, 0, lparam);
                NativeMethods.SendMessage(windowHandle, NativeMethods.TTM_TRACKACTIVATE, 1, toolinfo);
            }
            finally
            {
                if (IntPtr.Zero != toolinfo.lpszText)
                    Marshal.FreeHGlobal(toolinfo.lpszText);
            }
        }

        /// <summary>
        /// 关闭鼠标提示
        /// </summary>
        public void Close()
        {
            this.Hide();
        }

        private void TimerHandler(object sender, EventArgs e)
        {
            this.Hide();
            /*
                        millisecond += this.timer.Interval;
                        if (!hasShowed && millisecond>=this.InitialDelay) //只显示一次，不再重复显示
                        {
                            hasShowed = true;//已经显示过
                            this.Show(null, this.Text, GToolTipIcon.Null, Location);
                        }
                        else
                        {
                            if (this.AutoCloseDelay <= millisecond - this.InitialDelay)
                            {
                                this.CanShow = false;
                            }
                        }
            */
        }

        private void WndProc(ref Message msg)
        {
            switch (msg.Msg)
            {
                case WM_NOTIFY:
                    var nmhdr = (NativeMethods.NMHDR)msg.GetLParam(typeof(NativeMethods.NMHDR));
                    if (nmhdr.code == NativeMethods.TTN_SHOW)
                    {
                        var location = EnsureInScreen(this.Location, _tipSize);
                        NativeMethods.SetWindowPos(this.window.Handle, NativeMethods.HWND_TOPMOST,
                            location.X, location.Y, _tipSize.Width, _tipSize.Height,
                            NativeMethods.SWP_NOACTIVATE | NativeMethods.SWP_NOOWNERZORDER);
                        msg.Result = new IntPtr(1);
                    }

                    break;

                case WM_PAINT:
                    this.WmPaint(ref msg);
                    break;

                default:
                    this.window.DefWndProc(ref msg);
                    break;
            }
        }

        /// <summary>
        /// 鼠标提示渲染参数，内部使用。目前不开放自绘。
        /// </summary>
        protected class XToolTipRenderEventArgs : EventArgs
        {
            public Graphics Graphics { get; private set; }
            public Rectangle ClipBounds { get; private set; }
            public bool EraseBackground { get; private set; }

            /// <summary>初始化 <see cref="T:System.EventArgs" /> 类的新实例。</summary>
            public XToolTipRenderEventArgs(Graphics graphics, Rectangle clipBounds, bool eraseBackground)
            {
                Graphics = graphics;
                ClipBounds = clipBounds;
                EraseBackground = eraseBackground;
            }
        }

        /// <summary>
        /// 绘制鼠标提示内容
        /// </summary>
        /// <param name="e">鼠标提示渲染参数</param>
        protected virtual void OnPaint(XToolTipRenderEventArgs e)
        {
            var g = e.Graphics;
            var bounds = e.ClipBounds;
            // background color
            if (e.EraseBackground)
            {
                //g.Clear(this.BackColor);
                g.FillRectangle(new SolidBrush(this.backColor), bounds);
            }
            // border
            using (Pen pen = new Pen(BorderColor))
            {
                g.DrawRectangle(pen, new Rectangle(bounds.X, bounds.Y,
                    bounds.Width - 1, bounds.Height - 1));
            }
            // inflate the bounds and paddings
            bounds.Inflate(-3, -3);
            bounds.X += 1;
            Rectangle rectContent = bounds;
            Rectangle rectText = rectContent;
            g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
            using (var brush = new SolidBrush(ForeColor))
            {
                if (!string.IsNullOrEmpty(Title))
                {
                    // Specify custom text formatting flags.
                    using (Font titleFont = new Font(Font, FontStyle.Bold))
                    using (var sf = CreateTitleStringFormat())
                    {
                        //标题
                        var titleRect = new Rectangle(rectContent.Location, _titleSize);
                        g.DrawString(Title, titleFont, brush, titleRect, sf);
                    }
                    rectText = new Rectangle(rectContent.X, rectContent.Y + _titleSize.Height,
                        rectContent.Width, rectContent.Height - _titleSize.Height);
                }
                //文本
                using (var sf = CreateStringFormat())
                {
                    g.DrawString(Text, Font, brush, rectText, sf);
                }
            }
        }

        private void WmPaint(ref Message m)
        {
            if (ownerDraw)
            {
                NativeMethods.PAINTSTRUCT ps = new NativeMethods.PAINTSTRUCT();
                IntPtr dc = NativeMethods.BeginPaint(new HandleRef(this, Handle), ref ps);
                try
                {
                    var clip = new Rectangle(ps.rcPaint_left, ps.rcPaint_top,
                        ps.rcPaint_right - ps.rcPaint_left,
                        ps.rcPaint_bottom - ps.rcPaint_top);
                    if (clip == Rectangle.Empty)
                        return;

                    using (Graphics g = Graphics.FromHdcInternal(dc))
                    {
                        g.SetClip(clip);
                        var args = new XToolTipRenderEventArgs(g, clip, ps.fErase);
                        OnPaint(args);
                    }
                }
                finally
                {
                    NativeMethods.EndPaint(new HandleRef(this, Handle), ref ps);
                }
            }
            else
            {
                this.window.DefWndProc(ref m);
            }
        }

        private NativeMethods.TOOLINFO_TOOLTIP GetMinTOOLINFO(IntPtr uId)
        {
            NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP();
            ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP));
            ti.uFlags = NativeMethods.TTF_TRACK;
            ti.uId = uId;
            return ti;
        }

        private NativeMethods.TOOLINFO_TOOLTIP GetTOOLINFO(IntPtr uId, string caption)
        {
            NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP();
            ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP));
            ti.uFlags = NativeMethods.TTF_TRACK;
            ti.hwnd = ti.uId = uId;
            ti.lpszText = Marshal.StringToHGlobalAuto(caption);
            return ti;
        }

        private NativeMethods.TOOLINFO_TOOLTIP GetControlTOOLINFO(Control ctl, string caption)
        {
            NativeMethods.TOOLINFO_TOOLTIP ti = new NativeMethods.TOOLINFO_TOOLTIP();
            ti.cbSize = Marshal.SizeOf(typeof(NativeMethods.TOOLINFO_TOOLTIP));
            ti.uFlags = NativeMethods.TTF_TRACK;
            ti.hwnd = ctl.Handle;
            ti.uId = ctl.Handle;
            ti.lpszText = Marshal.StringToHGlobalAuto(caption);

            return ti;
        }

        private void CreateHandle()
        {
            if (IsHandleCreated)
                return;
            CreateParams cp = CreateParams;
            if (IsHandleCreated)
                return;
            window.CreateHandle(cp);
            // remove border
            if (ownerDraw)
            {
                int style = unchecked((int)((long)NativeMethods.GetWindowLong(new HandleRef(this, Handle), GWL_STYLE)));
                style &= ~WS_BORDER;
                NativeMethods.SetWindowLong(new HandleRef(this, Handle), GWL_STYLE, new HandleRef(null, (IntPtr)style));
            }
            // remove shadow
            if (!HasDropShadow)
            {
                int classLong = unchecked((int)((long)NativeMethods.GetClassLong(new HandleRef(this, Handle), NativeMethods.GCL_STYLE)));
                classLong &= ~NativeMethods.CS_DROPSHADOW;
                NativeMethods.SetClassLong(new HandleRef(this, Handle), NativeMethods.GCL_STYLE, (IntPtr)classLong);
            }
            // Set active status
            //
            NativeMethods.SendMessage(this.Handle, NativeMethods.TTM_ACTIVATE, (canShow == true) ? 1 : 0, 0);
            if (BackColor != SystemColors.Info)
            {
                NativeMethods.SendMessage(this.Handle, NativeMethods.TTM_SETTIPBKCOLOR, ColorTranslator.ToWin32(BackColor), 0);
            }
            if (ForeColor != SystemColors.InfoText)
            {
                NativeMethods.SendMessage(this.Handle, NativeMethods.TTM_SETTIPTEXTCOLOR, ColorTranslator.ToWin32(ForeColor), 0);
            }
            if (toolTipIcon > 0 || !String.IsNullOrEmpty(_title))
            {
                // If the title is null/empty, the icon won't display.
                string titleText = !String.IsNullOrEmpty(this._title) ? this._title : " ";
                NativeMethods.SendMessage(this.Handle, NativeMethods.TTM_SETTITLE, (int)toolTipIcon, titleText);
            }
        }

        protected virtual CreateParams CreateParams
        {
            //[SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            //[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            get
            {
                CreateParams cp = new CreateParams();

                cp.ClassName = NativeMethods.TOOLTIPS_CLASS;
                //不管是否指定，Tooltip总是有WS_POPUP和WS_EX_TOOLWINDOW
                //cp.Style = API.WS_POPUP;
                //cp.Style = API.TTS_BALLOON | API.WS_POPUP;//|gowk.common.API.TTS_ALWAYSTIP|2|0x0010|0x0100;
                if (showAlways)
                {
                    cp.Style |= NativeMethods.TTS_ALWAYSTIP;
                }
                if (isBalloon)
                {
                    cp.Style |= NativeMethods.TTS_BALLOON;
                }
                cp.Style |= NativeMethods.TTS_NOPREFIX;
                if (!useAnimation)
                {
                    cp.Style |= NativeMethods.TTS_NOANIMATE;
                }
                if (!useFading)
                {
                    cp.Style |= NativeMethods.TTS_NOFADE;
                }
                cp.ExStyle = 0;
                cp.ExStyle |= NativeMethods.WS_EX_TRANSPARENT;
                if (_topMost)
                {
                    cp.ExStyle |= NativeMethods.WS_EX_TOPMOST;
                }
                cp.Caption = null;
                return cp;
            }
        }

        protected void DestroyHandle()
        {
            if (IsHandleCreated)
            {
                window.DestroyHandle();
            }
        }

        private void RecreateHandle()
        {
            if (!DesignMode)
            {
                if (IsHandleCreated)
                {
                    StopTimer();
                    DestroyHandle();
                }
                CreateHandle();
            }
        }

        private void StartTimer()
        {
            this.StartTimer(AutoCloseDelay);
        }

        private void StartTimer(int interval)
        {
            if (_timer == null)
            {
                _timer = new Timer();
                // Add the timer handler
                _timer.Tick += new EventHandler(this.TimerHandler);
            }
            _timer.Interval = interval;
            _timer.Start();
        }

        /// <devdoc>
        ///     Stops the timer for hiding Positioned ToolTips
        /// </devdoc>
        protected void StopTimer()
        {
            Timer timerRef = _timer;
            if (timerRef != null)
            {
                timerRef.Stop();
                timerRef.Dispose();
                _timer = null;
            }
        }

        /// <summary>
        /// 获取鼠标提示字体
        /// </summary>
        /// <returns></returns>
        private Font GetToolTipFont()
        {
            return Font ?? Control.DefaultFont;
            //if (!IsHandleCreated)
            //{
            //    return Control.DefaultFont;
            //}
            //Font font;
            //try
            //{
            //    font = Font.FromHfont(NativeMethods.SendMessage(this.Handle, WM_GETFONT, 0, 0));
            //}
            //catch (ArgumentException)
            //{
            //    // VSWhidbey 209345 - if the current default tooltip font is a non-TrueType font, then
            //    // Font.FromHfont throws this exception, so fall back to the default control font.
            //    font = Control.DefaultFont;
            //}
            //finally
            //{
            //    //CodeAccessPermission.RevertAssert();
            //}
            //return font;
        }

        /// <summary>
        /// 计算鼠标提示大小
        /// </summary>
        /// <returns>计算好的鼠标提示大小</returns>
        protected virtual Size GetToolTipSize()
        {
            if (!IsHandleCreated)
                return Size.Empty;
            // 字体
            Font font = GetToolTipFont();
            // border + padding
            Size size = new Size(7, 6);
            _titleSize = new Size();
            if (!string.IsNullOrEmpty(Title))
            {
                // Specify custom text formatting flags.
                using (Font titleFont = new Font(font, FontStyle.Bold))
                {
                    _titleSize = GetPreferredSize(new Size(this.MaxTipWidth, 0), new Size(0, 0), Title, titleFont);
                }
            }
            _textSize = GetPreferredSize(new Size(this.MaxTipWidth, 0), new Size(0, 0), Text, font);
            _textSize += new Size(0, 1);
            size += new Size(Math.Max(_textSize.Width, _titleSize.Width), _textSize.Height + _titleSize.Height);
            return size;
        }

        private StringFormat CreateStringFormat()
        {
            var sf = new StringFormat(0);
            sf.Alignment = StringAlignment.Near;
            sf.LineAlignment = StringAlignment.Center;
            return sf;
        }

        private StringFormat CreateTitleStringFormat()
        {
            var sf = new StringFormat(0);
            sf.Alignment = StringAlignment.Near;
            sf.LineAlignment = StringAlignment.Near;
            return sf;
        }

        /// <summary>
        /// 检索可以容纳控件的矩形区域的大小。
        /// </summary>
        /// <param name="proposedSize">控件的自定义大小的区域。</param>
        /// <param name="borderAndPaddingSize"></param>
        /// <param name="text"></param>
        /// <param name="font"></param>
        /// <returns>类型为 System.Drawing.Size 的有序对，表示矩形的宽度和高度。</returns>
        private Size GetPreferredSize(Size proposedSize, Size borderAndPaddingSize, string text, Font font)
        {
            //make sure the behavior is consistent with GetPreferredSizeCore
            if (proposedSize.Width == 1)
            {
                proposedSize.Width = 0;
            }
            if (proposedSize.Height == 1)
            {
                proposedSize.Height = 0;
            }

            //将0转换为无限制
            //proposedSize = LayoutUtils.ConvertZeroToUnbounded(proposedSize);

            if (proposedSize.Width == 0) proposedSize.Width = Int32.MaxValue;
            if (proposedSize.Height == 0) proposedSize.Height = Int32.MaxValue;

            //var borderAndPaddingSize = new Size(8, 8);
            Size bordersAndPadding = borderAndPaddingSize;
            // Subtract border area from constraints
            proposedSize -= bordersAndPadding;

            // keep positive
            proposedSize = new Size(
                Math.Max(proposedSize.Width, 0),
                Math.Max(proposedSize.Height, 0));
            Size requiredSize;
            if (string.IsNullOrEmpty(text))
            {
                requiredSize = new Size(0, font.Height);
            }
            else
            {
                using (Graphics g = Graphics.FromHwnd(NativeMethods.NullHandleRef.Handle))
                using (StringFormat stringFormat = CreateStringFormat())
                {
                    SizeF bounds = new SizeF(
                        (proposedSize.Width == 1) ? 0 : proposedSize.Width,
                        Math.Max(proposedSize.Height, font.GetHeight(g)));
                    requiredSize = Size.Ceiling(g.MeasureString(text, font, bounds, stringFormat));
                }
            }
            requiredSize += bordersAndPadding;
            return requiredSize;
        }

        /// <summary>
        /// 根据参考矩形和鼠标提示大小，以及相对位置，计算鼠标提示的坐标
        /// </summary>
        /// <param name="targetBounds"></param>
        /// <param name="tipSize"></param>
        /// <param name="targetLocation"></param>
        /// <param name="tipLocation"></param>
        /// <returns></returns>
        private Point GetRelativePoint(Rectangle targetBounds, Size tipSize, Position targetLocation,
            Position tipLocation)
        {
            //左上
            Point targetPoint = targetBounds.Location;
            //中心
            Point targetCenterOffset = new Point((targetBounds.Width) / 2, (targetBounds.Height) / 2);

            switch (targetLocation)
            {
                case Position.Center:
                    targetPoint.Offset(targetCenterOffset);
                    break;

                case Position.TopLeft:
                    // this is it !
                    break;

                case Position.Top:
                    targetPoint.Offset(targetCenterOffset.X, 0);
                    break;

                case Position.TopRight:
                    targetPoint.Offset(targetBounds.Width, 0);
                    break;

                case Position.BottomLeft:
                    targetPoint.Offset(0, targetBounds.Height);
                    break;

                case Position.Bottom:
                    targetPoint.Offset(targetCenterOffset.X, targetBounds.Height);
                    break;

                case Position.BottomRight:
                    targetPoint.Offset(targetBounds.Width, targetBounds.Height);
                    break;

                case Position.Left:
                    targetPoint.Offset(0, targetCenterOffset.Y);
                    break;

                case Position.Right:
                    targetPoint.Offset(targetBounds.Width, targetCenterOffset.Y);
                    break;
            }

            Point tipCenter = new Point(tipSize.Width / 2, tipSize.Height / 2);

            switch (tipLocation)
            {
                case Position.Center:
                    targetPoint.Offset(-tipCenter.X, -tipCenter.Y);
                    break;

                case Position.TopLeft:
                    // this is it !
                    break;

                case Position.Top:
                    targetPoint.Offset(-tipCenter.X, 0);
                    break;

                case Position.TopRight:
                    targetPoint.Offset(-tipSize.Width, 0);
                    break;

                case Position.BottomLeft:
                    targetPoint.Offset(0, -tipSize.Height);
                    break;

                case Position.Bottom:
                    targetPoint.Offset(-tipCenter.X, -tipSize.Height);
                    break;

                case Position.BottomRight:
                    targetPoint.Offset(-tipSize.Width, -tipSize.Height);
                    break;

                case Position.Left:
                    targetPoint.Offset(0, -tipCenter.Y);
                    break;

                case Position.Right:
                    targetPoint.Offset(-tipSize.Width, -tipCenter.Y);
                    break;
            }

            return targetPoint;
        }

        /// <summary>
        /// 确保不要超出屏幕。当超出屏幕时，修正左上角坐标。
        /// </summary>
        /// <param name="location">左上角坐标</param>
        /// <param name="size">大小</param>
        private static Point EnsureInScreen(Point location, Size size)
        {
            //以指定位置坐标获取所在屏幕的工作区
            Rectangle screen = Screen.FromPoint(location).WorkingArea;
            //右下角坐标
            Point bottomRight = location + size;
            //超出屏幕
            if (bottomRight.X > screen.Right)
                location.X = screen.Right - size.Width;
            if (bottomRight.Y > screen.Bottom)
                location.Y = screen.Bottom - size.Height;

            if (location.X < screen.Left)
                location.X = screen.Left;
            if (location.Y < screen.Top)
                location.Y = screen.Top;
            return location;
        }

        #endregion Method

        #region Dispose

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                var timer = _timer;
                if (timer != null)
                {
                    timer.Dispose();
                    _timer = null;
                }
            }

            DestroyHandle();
            window = null;

            base.Dispose(disposing);
        }

        #endregion Dispose

        private class TootipNativeWindow : NativeWindow
        {
            private readonly XToolTipControl _tip;

            public TootipNativeWindow(XToolTipControl tip)
            {
                this._tip = tip;
            }

            protected override void WndProc(ref Message m)
            {
                this._tip.WndProc(ref m);
            }
        }

        #region WinAPI Consts

        public const int WM_NOTIFY = 0x4e;
        public const int WM_PAINT = 15;

        public const int GWL_EXSTYLE = -20;
        public const int GWL_HWNDPARENT = -8;
        public const int GWL_ID = -12;
        public const int GWL_STYLE = -16;
        public const int GWL_WNDPROC = -4;

        public const int WS_BORDER = 0x800000;
        public const int WS_CAPTION = 0xc00000;
        public const int WS_CHILD = 0x40000000;
        public const int WS_CLIPCHILDREN = 0x2000000;
        public const int WS_CLIPSIBLINGS = 0x4000000;
        public const int WS_DISABLED = 0x8000000;
        public const int WS_DLGFRAME = 0x400000;
        public const int WS_EX_APPWINDOW = 0x40000;
        public const int WS_EX_CLIENTEDGE = 0x200;
        public const int WS_EX_COMPOSITED = 0X02000000;
        public const int WS_EX_CONTEXTHELP = 0x400;
        public const int WS_EX_CONTROLPARENT = 0x10000;
        public const int WS_EX_DLGMODALFRAME = 1;
        public const int WS_EX_LAYERED = 0x80000;
        public const int WS_EX_LEFT = 0;
        public const int WS_EX_LEFTSCROLLBAR = 0x4000;
        public const int WS_EX_MDICHILD = 0x40;
        public const int WS_EX_RIGHT = 0x1000;
        public const int WS_EX_RTLREADING = 0x2000;
        public const int WS_EX_STATICEDGE = 0x20000;
        public const int WS_EX_TOOLWINDOW = 0x80;
        public const int WS_EX_TOPMOST = 8;
        public const int WS_HSCROLL = 0x100000;
        public const int WS_MAXIMIZE = 0x1000000;
        public const int WS_MAXIMIZEBOX = 0x10000;
        public const int WS_MINIMIZE = 0x20000000;
        public const int WS_MINIMIZEBOX = 0x20000;
        public const int WS_OVERLAPPED = 0;
        public const int WS_POPUP = -2147483648;
        public const int WS_SYSMENU = 0x80000;
        public const int WS_TABSTOP = 0x10000;
        public const int WS_THICKFRAME = 0x40000;
        public const int WS_VISIBLE = 0x10000000;
        public const int WS_VSCROLL = 0x200000;

        public const int WM_GETDLGCODE = 0x87;
        public const int WM_GETFONT = 0x31;
        public const int WM_GETHOTKEY = 0x33;
        public const int WM_GETICON = 0x7f;
        public const int WM_GETMINMAXINFO = 0x24;
        public const int WM_GETOBJECT = 0x3d;
        public const int WM_GETTEXT = 13;
        public const int WM_GETTEXTLENGTH = 14;

        #endregion WinAPI Consts
    }

    /// <summary>
    /// 相对位置
    /// </summary>
    public enum Position
    {
        /// <summary>
        /// 正中
        /// </summary>
        Center,

        /// <summary>
        /// 上方中间位置
        /// </summary>
        Top,

        /// <summary>
        /// 右侧中间位置
        /// </summary>
        Right,

        /// <summary>
        /// 下方中间位置
        /// </summary>
        Bottom,

        /// <summary>
        /// 左侧中间位置
        /// </summary>
        Left,

        /// <summary>
        /// 左上角
        /// </summary>
        TopLeft,

        /// <summary>
        /// 右上角
        /// </summary>
        TopRight,

        /// <summary>
        /// 左下角
        /// </summary>
        BottomLeft,

        /// <summary>
        /// 右下角
        /// </summary>
        BottomRight,
    }
}